#ifndef __EKU_SYSCALL_H__
#define __EKU_SYSCALL_H__

#include <syscall.h>

static __inline int syscall0(uint32_t num) __attribute__((always_inline));
static __inline int syscall1(uint32_t num, uint32_t a) __attribute__((always_inline));
static __inline int syscall2(uint32_t num, uint32_t a, uint32_t b) __attribute__((always_inline));
static __inline int syscall3(uint32_t num, uint32_t a, uint32_t b, uint32_t c) __attribute__((always_inline));
static __inline int syscall4(uint32_t num, uint32_t a, uint32_t b, uint32_t c, uint32_t d) __attribute__((always_inline));
static __inline int syscall5(uint32_t num, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e) __attribute__((always_inline));

static __inline int
syscall0(uint32_t num)
{
	 int result;
	 __asm __volatile("int %1\nmovl %%eax, %0"
					  : "=m"(result)
					  : "n"(INT_SYSCALL), "a"(num));
	 return result;
}

static __inline int
syscall1(uint32_t num, uint32_t a)
{
	 int result;
	 __asm __volatile("int %1\nmovl %%eax, %0"
					  : "=m"(result)
					  : "n"(INT_SYSCALL), "a"(num), "b"(a));
	 return result;
}

static __inline int
syscall2(uint32_t num, uint32_t a, uint32_t b)
{
	 int result;
	 __asm __volatile("int %1\nmovl %%eax, %0"
					  : "=m"(result)
					  : "n"(INT_SYSCALL), "a"(num), "b"(a), "c"(b));
	 return result;
}

static __inline int
syscall3(uint32_t num, uint32_t a, uint32_t b, uint32_t c)
{
	 int result;
	 __asm __volatile("int %1\nmovl %%eax, %0"
					  : "=m"(result)
					  : "n"(INT_SYSCALL), "a"(num), "b"(a), "c"(b), "d"(c));
	 return result;
}

static __inline int
syscall4(uint32_t num, uint32_t a, uint32_t b, uint32_t c, uint32_t d)
{
	 int result;
	 __asm __volatile("int %1\nmovl %%eax, %0"
					  : "=m"(result)
					  : "n"(INT_SYSCALL), "a"(num), "b"(a), "c"(b), "d"(c), "S"(d));
	 return result;
}

static __inline int
syscall5(uint32_t num, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e)
{
	 int result;
	 __asm __volatile("int %1\nmovl %%eax, %0"
					  : "=m"(result)
					  : "n"(INT_SYSCALL), "a"(num), "b"(a), "c"(b), "d"(c), "S"(d), "D"(e));
	 return result;
}

#include <stdarg.h>

/* alias of system calls */
static inline void
sc_yield(void)
{
	 __asm __volatile("int %0"
					  :
					  : "n"(INT_YIELD));
}

static inline void
sc_kprintf(const char *fmt, ...)
{
     va_list va;
     va_start(va, fmt);
     syscall2(SC_KPRINTF, (uint32_t)fmt, (uint32_t)va);
     va_end(va);
}

static inline void
sc_kvprintf(const char *fmt, va_list va)
{
     syscall2(SC_KPRINTF, (uint32_t)fmt, (uint32_t)va);
}


static inline int
sc_send(int ap, int ips, uint64_t arg)
{
	 return syscall4(SC_SEND, (uint32_t)ap, (uint32_t)ips, (uint32_t)arg, (uint32_t)(arg >> 32));
}

static inline int
sc_send_try(int ips, int wait)
{
	 return syscall2(SC_SEND_TRY, (uint32_t)ips, (uint32_t)wait);
}

static inline int
sc_send_break(int ips)
{
	 return syscall1(SC_SEND_BREAK, (uint32_t)ips);
}

static inline int
sc_wait_try(void)
{
	 syscall0(SC_WAIT_TRY);
}

static inline void
sc_wait_pretend()
{
	 syscall0(SC_WAIT_PRETEND);
}

static inline int
sc_wait()
{
	 syscall0(SC_WAIT);
}

static inline int
sc_notify(int proc)
{
	 return syscall1(SC_NOTIFY, (uint32_t)proc);
}

static inline int
sc_set_proc_attr(uint32_t index, uint32_t value)
{
	 return syscall2(SC_SET_PROC_ATTR, index, value);
}

static inline int
sc_signal(int proc)
{
	 return syscall1(SC_SIGNAL, (uint32_t)proc);
}

static inline void
sc_exit(int ret)
{
	 syscall1(SC_EXIT, (uint32_t)ret);
}

static inline int
sc_recv(int ap, int ips)
{
	 return syscall2(SC_RECV, (uint32_t)ap, (uint32_t)ips);
}

static inline int
sc_recv_try(int ips, int wait, uint64_t *arg_ptr)
{
	 int result;
	 uint32_t lo, hi;
	 __asm __volatile("int %3\nmovl %%eax, %0\nmovl %%ebx, %1\nmovl %%ecx, %2"
					  : "=m"(result), "=m"(lo), "=m"(hi)
					  : "n"(INT_SYSCALL), "a"(SC_RECV_TRY), "b"(ips), "c"(wait));	 
	 if (result >= 0 && arg_ptr != NULL)
	 {
		  *arg_ptr = ((uint64_t)hi << 32) | lo;
	 }
	 return result;
}

static inline int
sc_recv_break(int ips)
{
	 return syscall1(SC_RECV_BREAK, (uint32_t)ips);
}

static inline int
sc_notify_ex(int pid, int level)
{
	 return syscall2(SC_NOTIFY_EX, (uint32_t)pid, (uint32_t)level);
}

static inline int
sc_proc_fork(int pid, void *func, void *stack)
{
	 return syscall3(SC_PROC_FORK, (uint32_t)pid, (uint32_t)func, (uint32_t)stack);
}

static inline int
sc_proc_attach(int pid)
{
	 return syscall1(SC_PROC_ATTACH, (uint32_t)pid);
}

static inline int
sc_proc_detach(int pid)
{
	 return syscall1(SC_PROC_DETACH, (uint32_t)pid);
}

static inline int
sc_proc_kill(int pid)
{
	 return syscall1(SC_PROC_KILL, (uint32_t)pid);
}

static inline int
sc_mmap_set(int pid, void *vaddr, physaddr_t paddr, int perm)
{
	 return syscall4(SC_MMAP_SET, (uint32_t)pid, (uint32_t)vaddr, (uint32_t)paddr, (uint32_t)perm);
}

static inline int
sc_ppage_alloc(int num, int dma, physaddr_t *addr_ptr)
{
	 int result;
	 uint32_t addr;
	 __asm __volatile("int %2\nmovl %%eax, %0\nmovl %%ebx, %1"
					  : "=m"(result), "=m"(addr)
					  : "n"(INT_SYSCALL), "a"(SC_PPAGE_ALLOC), "b"(num), "c"(dma));
	 if (result >= 0) *addr_ptr = addr;
	 return result;
}

static inline int
sc_ppage_free(physaddr_t addr)
{
	 return syscall1(SC_PPAGE_FREE, addr);
}

static inline int
sc_set_soft_timer(uint32_t alarm, int pid)
{
	 return syscall2(SC_SET_SOFT_TIMER, alarm, pid);
}

#endif
